#ifndef __ATOMTRACE_UTIL_H_
#define __ATOMTRACE_UTIL_H_

#include <trax.hpp>
#include "atAtomicReg.h"

namespace AtomTrace{
	class Vector3;

	const Vector3 GetCrossNormal(const Vector3 & v1, const Vector3 & v2);
	// map linear increment (1, 2, 3...) to z-shape index on 2d array
    void ConvertLineIndexToZindexXY(int ival, int & ox, int & oy);
    
    // map linear increment (1, 2, 3...) to z-shape index on 3d coordinate
    void ConvertZcurveIndex(int ival, Vector3 & pos, int p = 0);
    //inline void ConvertZcurveIndex(int ival, Vector3 & pos, int p = 0){}

    // map 3d coordinate to z-curve 1d index
    //\p: precision
    //\Problem: what if negative vector3?
    void GetZcurveIndex(const Vector3& pos, int & o_index, int p = 0);
    //inline void GetZcurveIndex(const Vector3& pos, int & o_index, int p = 0){}

    const Vector3 ReflectionDir(const Vector3 & n, const Vector3 & rev_incDir);

    /*
     * \param:
     * Vector3 n: surface normal;
     * Vector3 rev_incDir: incoming direction reverse;
     * float ni: refraction index;
     * bool * totRef: flag for whether total reflection
     */
    const Vector3 RefractionDir(const Vector3 & n, const Vector3 & rev_incDir, const float & ni, bool * totRef=0);

    Vector3 RandomHemiDirection(const Vector3 & n);
    Vector3 RandomDirection();
    
    Vector3 GetOrtho(const Vector3 & v);

    inline float absf(float ival);
    inline float Halton(int index, int base);

    inline int GetMemoryEndAddr();
    inline int GetMemorySize();
    
    /*
     * regID from 0 to 7
     */
    inline int GetRegisterValue(int regID);
    
    inline void ResetRegister(int regID);

    //****************** Inline function definition ********************

	float absf(float ival)
	{
		if(ival < 0.0f) return -ival;
		return ival;
	}

    float Halton(int index, int base)
    {
        float r = 0;
        float baseF_inv = static_cast<float>(base);
        float f = 1.0f / baseF_inv;
        baseF_inv = f;
        
        for(int i=index; i>0; i /= base)
        {
            r += f * (i%base);
            f *= baseF_inv;
        }

        return r;
    }

    int GetMemoryEndAddr()
    {
    	return loadi(0, TRAX_END_MEMORY);
    }

    int GetMemorySize()
    {
    	return loadi(0, TRAX_MEM_SIZE);
    }
    
    int GetRegisterValue(int regID)
    {
        if(regID > 7 || regID < 0)
        {
            printf("Error register id: %d\n", regID);
            return 0;
        }
        return atomicadd(regID, 0);
    }
    
    void ResetRegister(int regID)
    {
        int tid = GetThreadID();
        int tmid = GetCoreID();
        int l2id = GetL2ID();
        
        if(tid == tmid == l2id == 0)
        {
            if(regID == REG_BARRIER)
                printf("Warring: you are resting barrier register, would it be a problem?\n");
            int val = GetRegisterValue(regID);
            atomicadd(regID, -val);
        }
        barrier(REG_BARRIER);
    }
}

#endif // __ATOMTRACE_UTIL_H_
